/**
 *
 * The MIT License
 *
 * Copyright (c) 2008 Board of Trustees, Leland Stanford Jr. University. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.macdadi.ks.client.views;

import java.util.ArrayList;
import java.util.HashMap;

import org.macdadi.core.client.auth.AuthService;
import org.macdadi.core.client.auth.AuthenticatedUser;
import org.macdadi.core.client.auth.AuthorizationException;
import org.macdadi.core.client.domain.DataService;
import org.macdadi.core.client.domain.DataWriteService;
import org.macdadi.core.client.domain.Goal;
import org.macdadi.core.client.domain.Preference;
import org.macdadi.core.client.domain.ProjectInfo;
import org.macdadi.core.client.domain.StakeholderGroup;
import org.macdadi.core.client.domain.User;
import org.macdadi.core.client.ui.DefaultItemEditor;
import org.macdadi.core.client.ui.EditableTree;
import org.macdadi.ks.client.View;
import org.macdadi.ks.client.ViewContainer;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.TreeListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.gwtext.client.widgets.Component;
import com.gwtext.client.core.EventObject;
import com.gwtext.client.core.Ext;
import com.gwtext.client.core.ExtElement;
import com.gwtext.client.widgets.Button;
import com.gwtext.client.widgets.MessageBox;
import com.gwtext.client.widgets.Panel;
import com.gwtext.client.widgets.TabPanel;
import com.gwtext.client.widgets.MessageBox.PromptCallback;
import com.gwtext.client.widgets.event.ButtonListenerAdapter;
import com.gwtext.client.widgets.form.FormPanel;
import com.gwtext.client.widgets.form.TextField;
import com.gwtext.client.widgets.layout.AccordionLayout;
import com.gwtext.client.widgets.layout.BorderLayout;
import com.gwtext.client.widgets.layout.BorderLayoutData;
import com.gwtext.client.widgets.layout.ColumnLayoutData;
import com.gwtext.client.widgets.layout.FitLayout;
import com.gwtext.client.widgets.menu.BaseItem;
import com.gwtext.client.widgets.menu.Item;
import com.gwtext.client.widgets.menu.Menu;
import com.gwtext.client.widgets.menu.event.BaseItemListenerAdapter;
import com.gwtext.client.widgets.portal.Portal;
import com.gwtext.client.widgets.portal.PortalColumn;
import com.gwtext.client.widgets.portal.Portlet;
import com.gwtext.client.widgets.tree.TreeNode;
import com.gwtext.client.widgets.tree.TreePanel;
import com.gwtext.client.widgets.Window;
import com.gwtext.client.core.RegionPosition;
import com.gwtext.client.dd.DD;
import com.gwtext.client.dd.DropTarget;
/**
 * @author Ben Suter <ben.suter@gmail.com>
 */
public class Preferences extends View implements TreeListener
{
	protected VerticalPanel mainPanel, eastPanel;
	protected HorizontalPanel horizontalPanel, westPanel;

	protected static final String PREFIX_URL = "http://chart.apis.google.com/chart?";
	private static String instructionsString;
	private static String bugsString;

	protected EditableTree tree;
	protected ArrayList expandedItems = new ArrayList();
	protected ProjectInfo project;
	protected AuthenticatedUser currentUser;
	protected TreeNode[] checkedPrefGoals, checkedProjectMembers;
	protected boolean[] usedPanels;
	protected VerticalPanel[] panels;
	protected Goal[] secGoals = new Goal[100];
	protected String[] xLabels;
	protected User[] users;
	protected int[][] values;
	protected String value_names;
	protected EditableTree.ItemEditor preferenceEdit;
	protected Preference[] desired_values;
	protected HashMap prefsByGoal = new HashMap();
	protected final int maxBudget = 100;
	protected int remainingBudget, maxValue, goal_length;
	protected Portal portal;
	protected Portlet[] p;
	protected Item[] reset;
	protected boolean isRendered = false;
	Panel accordionPanel, tree_panel;
	Button resetButton, viewOthersChart, viewSelfChart, instructionsButton, editButton, submitButton;
	Window edit_window;
	int user_index = 0;
	int column_index = 0;
	int goal_index = 0;

	//TODO: Fails for larger sets of data. Investigate.

	public static ViewInfo init()
	{
		String info = "<p>Define your preferences for each goal</p>";

		return new ViewInfo("Preferences", info)
		{
			public View createInstance(ViewContainer container)
			{
				return new Preferences(container);
			}

			public String getColor()
			{
				return "#2fba10";
			}
		};
	}

	public Preferences(ViewContainer container)
	{
		this.container = container;
		project = container.getCurrentProject();

		mainPanel = new VerticalPanel();
		mainPanel.setStyleName("macdadi-Preferences");
		mainPanel.setWidth("98%");

		horizontalPanel = new HorizontalPanel();

		preferenceEdit = new PreferenceEdit(updateView, project);

		Panel space = new Panel();
		space.setHeight(20);
		space.setBorder(false);

		instructionsString = "<p> On this page, the user is given two options: </p>" +
		"<p> ~~~ </p> " +
		"<p><b>   1) User can see and edit his/her own preferences </b></p>" +
		"<p><b>   2) User can see the graphical representations of other user(s) </b></p>" +
		"<p> ~~~ </p> " +
		"<p> Remarks on the first bullet - In order for the bar chart to show up, the remaining budget MUST be 0. </p>" +
		"<p> Remarks on the second bullet - In the first tree of the Accordion Panel is a list of the Project's Members. By choosing a set of project members and then by choosing a set of goals you can filter out which goals you want to see on the bar chart as well as which user's preferences will affect this cumulative graph. </p>";
		bugsString = "<p> None ATM </p>" +
		"<p> ~~~ </p>" +
		"<p> Discover any bugs? Please contact me at: <i>jwei512@stanford.edu</i> </p>";

		HTML instructionsHTML = new HTML(instructionsString);

		TabPanel tabPanel = new TabPanel();  
		tabPanel.setActiveTab(0);

		Panel tab1 = new Panel();  
		tab1.setTitle("Instructions");  
		tab1.add(instructionsHTML);
		tab1.setAutoScroll(true);  

		Panel tab2 = new Panel();  
		tab2.setTitle("Current Bugs/Fixes");  
		tab2.setHtml(bugsString);  
		tab2.setAutoScroll(true);

		tabPanel.add(tab1);
		tabPanel.add(tab2);

		BorderLayoutData centerData = new BorderLayoutData(RegionPosition.CENTER);  
		centerData.setMargins(3, 0, 3, 3);

		final Window window = new Window();
		window.setTitle("Instructions for Preferences Tab");  
		window.setClosable(true);  
		window.setWidth(600);  
		window.setHeight(350);
		window.setPlain(true); 
		window.setLayout(new BorderLayout());  
		window.add(tabPanel, centerData);
		window.setCloseAction(Window.HIDE);

		instructionsButton = new Button("Instructions");  
		instructionsButton.addListener(new ButtonListenerAdapter() {
			public void onClick(Button button, EventObject e) {
				window.show();
			}  
		});

		mainPanel.add(instructionsButton);
		mainPanel.add(space);
		initWidget(mainPanel);
	}

	public void onShow() {
		currentUser = container.getCurrentUser();

		/* This view displays nothing if user is not authenticated */
		if ( currentUser == null )
			return;  
		accordionPanel = createAccordionPanel();
		Panel space = new Panel();
		space.setWidth(40);
		space.setBorder(false);

		resetButton = new Button();
		resetButton.setText("Reset");

		westPanel = new HorizontalPanel();
		westPanel.add(accordionPanel);

		horizontalPanel.add(westPanel);
		horizontalPanel.add(space);

		mainPanel.add(horizontalPanel);
	}

	public void onHide() { 	
		accordionPanel.clear();
		accordionPanel.destroy();
		tree.stopEditing();
		tree.removeItemEditor(preferenceEdit);
		tree.clear();
		goal_index = 0;
		for(int i = 0; i < 100; i++) {
			if(secGoals[i] == null) break;
			secGoals[i] = null;
		}
		for(int i = 0; i < usedPanels.length; i++) {
			usedPanels[i] = false;
		}
		for(int i = 0; i < panels.length; i++) {
			panels[i].clear();
		}
		portal.removeAll();
		horizontalPanel.remove(portal);
		horizontalPanel.clear();
		isRendered = false;
		if(edit_window.isVisible()) edit_window.close();
	}

	private Panel createAccordionPanel() {
		final Panel accordionPanel = new Panel();
		accordionPanel.setLayout(new AccordionLayout(true));
		accordionPanel.setId("accordion-preferences");
		accordionPanel.setHeight(500);
		accordionPanel.setWidth(325);
		accordionPanel.setAnimCollapse(true);

		final TreePanel selfPrefs = new selfPrefsTree();
		selfPrefs.setId("tree-selfpref");
		selfPrefs.setTitle("Your Preferences");
		selfPrefs.setWidth(325);  
		selfPrefs.setHeight(500);
		selfPrefs.setContainerScroll(true);
		selfPrefs.setAutoScroll(true);
		selfPrefs.setRootVisible(true);

		final TreePanel otherPrefs = new otherPrefsTree();
		otherPrefs.setId("tree-otherpref");
		otherPrefs.setTitle("Stakeholder Preferences on Goals");
		otherPrefs.setWidth(325);  
		otherPrefs.setHeight(500);
		otherPrefs.setContainerScroll(true);
		otherPrefs.setAutoScroll(true);
		otherPrefs.setRootVisible(true);

		final TreePanel treeGoals = new SampleGoalsTree();
		treeGoals.setId("tree-pref-goals");
		treeGoals.setTitle("Goals");
		treeGoals.setWidth(325);  
		treeGoals.setHeight(500);
		treeGoals.setContainerScroll(true);
		treeGoals.setAutoScroll(true);
		treeGoals.setRootVisible(true);

		viewOthersChart = new Button("Create Preference Chart");

		viewOthersChart.addListener(new ButtonListenerAdapter() {
			public void onClick(Button button, EventObject e) {
				checkedProjectMembers = otherPrefs.getChecked();
				checkedPrefGoals = treeGoals.getChecked();
				getCumulativePreferences(checkedPrefGoals, checkedProjectMembers);
			}
		});

		viewSelfChart = new Button("View Your Preference Chart");

		viewSelfChart.addListener(new ButtonListenerAdapter() {
			public void onClick(Button button, EventObject e) {
				if(remainingBudget == 0) {
					populateChart();
				} else {
					Window alert = new Window();
					alert.setWidth(300);
					alert.setHeight(300);
					alert.setPlain(false);
					alert.setHtml("<p><b><font size = 2> Remaining budget must be 0! </b></font></p>");
					alert.setClosable(true);
					alert.setDraggable(false);
					alert.setResizable(false);
					alert.setCloseAction(Window.HIDE);
					alert.show();
				}
			}
		});

		tree = new EditableTree(); 	
		tree_panel = new Panel();
		tree_panel.setHeight(320);
		tree_panel.add(tree);
		tree.addTreeListener(this);

		populateTree(project);
		if ( currentUser.isAuthorizedOperation(project.getId(), AuthService.TARGET_PREFERENCES, AuthService.OPERATION_UPDATE))
			tree.addItemEditor(preferenceEdit);

		edit_window = new Window();
		edit_window.setTitle("Edit Your Preferences");
		edit_window.setClosable(false);
		edit_window.setWidth(323);
		edit_window.setHeight(455);
		edit_window.setPlain(true);
		edit_window.setAutoScroll(true);
		edit_window.setPosition(35, 200);
		edit_window.add(tree_panel);
		edit_window.setDraggable(false);
		edit_window.setCloseAction(Window.HIDE);

		submitButton= new Button("Submit New Preferences");  
		submitButton.addListener(new ButtonListenerAdapter() {
			public void onClick(Button button, EventObject e) {
				tree.stopEditing();
				tree.removeItemEditor(preferenceEdit);
				tree.clear();
				tree_panel.destroy();
				edit_window.close();
				refreshSelfPrefs(selfPrefs, otherPrefs, treeGoals, accordionPanel);
			}
		});

		edit_window.addButton(submitButton);

		editButton = new Button("Edit Preferences");  
		editButton.addListener(new ButtonListenerAdapter() {  
			public void onClick(Button button, EventObject e) {
				edit_window.show();
			}
		});

		selfPrefs.addButton(editButton);
		selfPrefs.addButton(viewSelfChart);
		treeGoals.addButton(viewOthersChart);

		accordionPanel.add(otherPrefs);
		otherPrefs.collapse();
		accordionPanel.add(treeGoals);
		treeGoals.collapse();
		accordionPanel.add(selfPrefs);
		return accordionPanel;
	}

	public void getCumulativePreferences(final TreeNode[] checkedPrefGoals, final TreeNode[] checkedProjectMembers) {
		desired_values = new Preference[checkedPrefGoals.length];
		final Goal[] tempGoals = new Goal[checkedPrefGoals.length];
		final int[][] values = new int[checkedPrefGoals.length][checkedProjectMembers.length];

		DataService.App.getInstance().getLeafGoals(project.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
			public void onFailure(Throwable caught)
			{
				GWT.log("Failed to retrieve goals from database", caught);
				if(caught instanceof AuthorizationException) {
					System.out.println("Please login to obtain goals for " + project.getName());
				}
			}
			public void onSuccess(Object result)
			{
				Goal[] temp = (Goal[]) result;
				for(int i = 0; i < checkedPrefGoals.length; i++) {
					for(int j = 0; j < temp.length; j++) {
						if(checkedPrefGoals[i].getText().trim().equals(temp[j].getName().trim())) {
							tempGoals[i] = temp[j];
						}
					}
				}

				desired_values = createPrefArray(tempGoals, checkedPrefGoals);

				for(int i = 0; i < checkedProjectMembers.length; i++) {
					DataService.App.getInstance().getPreferencesForUser(project.getId(), Integer.parseInt(checkedProjectMembers[i].getId().trim()), container.getCurrentAuthToken(), new AsyncCallback() {
						public void onFailure(Throwable caught)
						{
							GWT.log("Failed to obtain the cumulative preferences", caught);
							if (caught instanceof AuthorizationException) {
								System.out.println("Please login to view cumulative preferences for " + project.getName());
							}
						}
						public void onSuccess(Object result) {
							Preference[] user_pref = (Preference[]) result;
							for(int i = 0; i < desired_values.length; i++) {
								desired_values[i].setValue(extractPreferenceValues(user_pref, desired_values[i]));
								values[i][column_index] = desired_values[i].getValue();
							}
							column_index++;
							user_index = drawPrefChart(user_index, column_index, values, desired_values);
						}
					});
					column_index = user_index = 0;
				}
			}
		});
	}

	//For other user prefs chart
	protected int drawPrefChart(int user_index, int column_index, int[][] values, Preference[] desired_values) {
		user_index++;
		if(user_index == checkedProjectMembers.length) {
			column_index = 0;
			String[] xLabels = new String[desired_values.length];
			for (int i = 0; i < desired_values.length; i++) {
				xLabels[desired_values.length-1-i] = desired_values[i].getGoal().getName();
			}

			String value_names = "";

			for(int i=0; i < desired_values.length; i++) {
				if(i == (desired_values.length-1)) {
					value_names = value_names + xLabels[i];
				} else
					value_names = value_names + xLabels[i] + "|";
			}

			String[] legendLabels = new String[checkedProjectMembers.length];
			for(int i = 0; i < checkedProjectMembers.length; i++) {
				legendLabels[i] = checkedProjectMembers[i].getText().trim();
			}

			String legend_names = "";

			for(int i=0; i < checkedProjectMembers.length; i++) {
				if(i == (checkedProjectMembers.length-1)) {
					legend_names = legend_names + legendLabels[i];
				} else
					legend_names = legend_names + legendLabels[i] + "|";
			}

			int max_value = 0; //calculates max sum value for any preference, this allows us to fix the scale of the graph
			for(int i = 0; i < desired_values.length; i++) {
				int temp_value = 0;
				for(int j = 0; j < checkedProjectMembers.length; j++) {
					temp_value += values[i][j];
				}
				if(temp_value > max_value)
					max_value = temp_value;
			}

			String chartData = "";

			for(int i=0; i < checkedProjectMembers.length; i++) {
				if(i == (checkedProjectMembers.length-1)) {
					for(int j = 0; j < desired_values.length; j++) {
						if(j == (desired_values.length-1)) {
							chartData = chartData + values[j][i];
						} else
							chartData = chartData + values[j][i] + ",";
					}
				} else {
					for(int j = 0; j < desired_values.length; j++) {
						if(j == (desired_values.length-1)) {
							chartData = chartData + values[j][i];
						} else
							chartData = chartData + values[j][i] + ",";
					}
					chartData = chartData + "|";
				}
			}

			if(desired_values.length * 30 > 325) {
				int numDelete = desired_values.length - 325/30;
				Window alert = new Window();
				alert.setWidth(310);
				alert.setHeight(400);
				alert.setPlain(false);
				alert.setHtml("<p><font size = 2> <b>WARNING</b>: Maximum graph size has been exceeded! </p> " +
						"<p>Too many options and goals have been selected. Please unselect " + numDelete + " goals/options. </p></font>" +
				"<img src = 'http://thumbs.dreamstime.com/thumb_376/1237296127yvKlEy.jpg' />");
				alert.setClosable(true);
				alert.setDraggable(false);
				alert.setResizable(false);
				alert.setCloseAction(Window.HIDE);
				alert.show();
				final ExtElement elm1 = Ext.get("tree-pref-goals");
				elm1.unmask();
			} else {
				String URL = "<img src = " + "'" + PREFIX_URL + "chxt=y,x&chs=900x325&cht=bhs&chd=t:" + chartData + "&chdl=" + legend_names + "&chco=4d89f9,48e766,f468ca,c6d9fd,e74863,5cb3ff,e4ec57,f52887,57ecbf&chdlp=r&chds=0," + (max_value - max_value%100 + 100) + "&chxl=0:|" + value_names + "|1:|0|" + (max_value - max_value%100 + 100)/2 + "|" + (max_value - max_value%100 + 100) + "'" + " />";
				HTML html = new HTML(URL);
				int index = returnAvailablePanel();
				if(index == -1) {
					Window alert = new Window();
					alert.setWidth(300);
					alert.setHeight(300);
					alert.setPlain(false);
					alert.setHtml("<p><b><font size = 2> Please clear one or more of your graphs. </b></font></p>" +
					"<p><img src = 'http://img.quamut.com/chart/771/14washing_windows.jpg' />");
					alert.setClosable(true);
					alert.setDraggable(false);
					alert.setResizable(false);
					alert.setCloseAction(Window.HIDE);
					alert.show();
				} else {
					panels[index].add(html);
				}
			}
			return -1;
		}
		return user_index;
	}

	private int returnAvailablePanel() {
		for(int i = 0; i < usedPanels.length; i++) {
			if(!usedPanels[i]) {
				usedPanels[i] = true;
				return i;
			}
		}
		return -1;
	}

	public Preference[] createPrefArray(Goal[] tempGoals, TreeNode[] checkedPrefGoals) {
		Preference[] tempPrefs = new Preference[0];
		for(int i = 0; i < checkedPrefGoals.length; i++) {
			Preference[] prefsDelete = new Preference[tempPrefs.length+1];
			Preference p = new Preference();
			p.setGoal(tempGoals[i]);
			for(int j = 0; j < tempPrefs.length; j++) {
				prefsDelete[j] = tempPrefs[j];
			}
			prefsDelete[tempPrefs.length] = p;
			tempPrefs = prefsDelete;
		}
		return tempPrefs;
	}

	public int extractPreferenceValues(Preference[] user_pref, Preference desired_values) {
		for(int i = 0; i < user_pref.length; i++) {
			if(user_pref[i].getGoal().getName().trim().equals(desired_values.getGoal().getName().trim()))
				return user_pref[i].getValue();
		}
		return 0;
	}

	public void refreshSelfPrefs(Panel selfPrefs, Panel otherPrefs, Panel treeGoals, Panel accordionPanel) {
		accordionPanel.destroy();
		goal_index = 0;
		for(int i = 0; i < p.length; i++) {
			p[i].setTitle(secGoals[i].getName());
		}
		for(int i = 0; i < usedPanels.length; i++) {
			usedPanels[i] = false;
		}
		for(int i = 0; i < panels.length; i++) {
			panels[i].clear();
		}
		accordionPanel = createAccordionPanel();
		otherPrefs.collapse();
		treeGoals.collapse();
		westPanel.add(accordionPanel);
	}

	class selfPrefsTree extends TreePanel {  

		public selfPrefsTree() {

			final TreeNode root = new TreeNode("Your Preferences");
			root.setExpanded(true);

			DataService.App.getInstance().getPreferencesForUser(project.getId(), currentUser.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
				public void onFailure(Throwable caught)
				{
					GWT.log("Failed to populate the preferences table", caught);
					if (caught instanceof AuthorizationException) {
						root.setText("Please login to view preferences for " + project.getName());
					}
				}

				public void onSuccess(Object result)
				{
					Preference[] prefs = (Preference[]) result;

					remainingBudget = maxBudget;
					prefsByGoal.clear();

					Preference pref;
					for (int i = 0; i < prefs.length; i++) {
						pref = prefs[i];
						prefsByGoal.put(Integer.toString(pref.getGoal().getId()), pref);
						remainingBudget -= pref.getValue();
					}
					loadGoalsWithPreferences(root);
				}
			});
			setTitle("Your Preferences");  
			setWidth(200);  
			setHeight(400);  
			setRootNode(root);
		}
	}

	protected void loadGoalsWithPreferences(final TreeNode rootItem) {
		DataService.App.getInstance().getGoals(project.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
			public void onFailure(Throwable caught) {
				GWT.log("Failed to populate the goals/preferences tree", caught);
				if (caught instanceof AuthorizationException) {
					rootItem.setText("Please login to view preferences for " + project.getName());
				}
			}

			public void onSuccess(Object result) {		
				rootItem.setText("Preferences for " + currentUser.getName() + " - Remaining Budget: " + remainingBudget);
				rootItem.setCls("accordion-bold");

				Goal[] roots = (Goal[]) result;
				for (int i = 0; i < roots.length; i++) {
					addChild(rootItem, roots[i], true);
				}

				/* Expand the first and second levels */
				rootItem.setExpanded(true);
				rootItem.expandChildNodes(false);
			}
		});
	}

	protected int addChild(TreeNode parent, Goal child, boolean recurse) {
		TreeNode node = new TreeNode();
		node.setUserObject(child);
		parent.appendChild(node);

		int aggregateValue = 0;
		if (recurse) {
			for (int i = 0; i < child.getNumChildren(); i++)
				aggregateValue += addChild(node, child.getChild(i), true);
		}


		Preference pref;
		pref = (Preference) prefsByGoal.get(Integer.toString(child.getId()));
		if ( pref != null) {
			/* If this goal has a specific preference assigned, its value is taken
			 * as the associated preference. Otherwise its value is taken as the
			 * aggregated values of its sub-goals.
			 */
			aggregateValue = pref.getValue();
		}

		String label;
		label = child.getName();
		label += ": " + aggregateValue;
		node.setText(label);

		/* If this node, as identified by its goal's id, was "expanded" in a
		 * previous incarnation of this tree, then expand it here again.
		 */
		if (expandedItems.contains(Integer.toString(child.getId())))
			node.setExpanded(true);

		return aggregateValue;
	}

	class otherPrefsTree extends TreePanel {  

		public otherPrefsTree() {

			final TreeNode root = new TreeNode();
			root.setText("Loading stakeholders for " + project.getName());
			root.setExpanded(true);

			DataService.App.getInstance().getStakeholderGroups(project.getId(), container.getCurrentAuthToken(), new AsyncCallback()
			{
				public void onFailure(Throwable caught)
				{
					GWT.log("Failed to populate the stakeholders tree", caught);

					if (caught instanceof AuthorizationException) {
						root.setText("Please login to view stakeholders for " + project.getName());
					}
				}

				public void onSuccess(Object result)
				{
					root.setText("Stakeholders for " + project.getName());

					StakeholderGroup[] roots = (StakeholderGroup[]) result;
					for (int i = 0; i < roots.length; i++) {
						addStakeholdersChild(root, roots[i]);
					}

					root.setExpanded(true);
					root.expandChildNodes(true);
				}
			});
			setTitle("List of Stakeholders");  
			setWidth(200);  
			setHeight(400);  
			setRootNode(root);   
		}
	}

	//Adds the groups.
	protected void addStakeholdersChild(TreeNode parent, StakeholderGroup child)
	{
		TreeNode node = new TreeNode(child.getName());
		node.setUserObject(child);
		parent.appendChild(node);

		for (int i = 0; i < child.getNumMembers(); i++)
			addStakeholdersChild(node, child.getMember(i));

		node.setExpanded(true);
	}

	//Adds the users in the groups.
	protected void addStakeholdersChild(TreeNode parent, User child)
	{
		TreeNode node = new TreeNode(child.getName());
		node.setUserObject(child);
		node.setId(Integer.toString(child.getId()).trim());
		node.setChecked(true);
		node.setUserObject(child);
		parent.appendChild(node);
		node.setExpanded(true);
	}

	class SampleGoalsTree extends TreePanel {  

		public SampleGoalsTree() {  

			final TreeNode root = new TreeNode("Goals");
			root.setExpanded(true);

			DataService.App.getInstance().getGoals(project.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
				public void onFailure(Throwable caught) {
					GWT.log("Failed to populate the goals tree", caught);
					if (caught instanceof AuthorizationException) {
						root.setText("Please login to view goals for " + project.getName());
					}
				}

				public void onSuccess(Object result) {
					root.setText("Goals");

					final Goal[] roots = (Goal[]) result;
					for (int i = 0; i < roots.length; i++) {
						addChildGoal(root,roots[i],true, roots[i].getNumChildren(),true);
					}
					int numSecondOrder = 0;
					for(int i = 0; i < secGoals.length; i++) {
						if(secGoals[i] == null) break;
						numSecondOrder++;
					}
					eastPanel = new VerticalPanel();
					if(!isRendered) {
						setUpReset(numSecondOrder);
						setUpPanels(numSecondOrder);
						usedPanels = new boolean[numSecondOrder];
						isRendered = true;
					}
					
					for(int i = 0; i < numSecondOrder; i++) {
						xLabels = new String[secGoals[i].getNumChildren()];
						for(int j = 0; j < secGoals[i].getNumChildren() ; j++) {
							xLabels[j] = secGoals[i].getChild(j).getName();
						}

						value_names = "";

						for(int j = 0; j < secGoals[i].getNumChildren(); j++) {
							if(j == (secGoals[i].getNumChildren()-1)) {
								value_names = value_names + xLabels[j];
							} else {
								value_names = value_names + xLabels[j] + "|";
							}
						}

						goal_length = secGoals[i].getNumChildren();
						createInitialGraphs(numSecondOrder, i, secGoals[i], value_names, goal_index, goal_length);
						goal_index += goal_length;
					}

					/* Expand the first and second levels */
					root.setExpanded(true);
					root.expandChildNodes(false);
				}
			});
			setTitle("Goals for Green Dorm Project");  
			setWidth(200);  
			setHeight(400);  
			setRootNode(root);
		}  
	}

	private void createInitialGraphs(final int numSecondOrder, final int index, final Goal secGoal, final String value_names, final int goal_index, final int goal_length) {	
		DataService.App.getInstance().getUsers(project.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
			public void onFailure(Throwable caught) {
				GWT.log("Failed to get project users", caught);
				if (caught instanceof AuthorizationException) {
					System.out.println("COULD NOT GET USERS");
				}
			}

			public void onSuccess(Object result) {
				users = (User[]) result;

				//Fill the values multidimensional-array
				DataService.App.getInstance().getPreferencesForAllUsers(project.getId(), users, container.getCurrentAuthToken(), new AsyncCallback() {
					public void onFailure(Throwable caught)
					{
						GWT.log("Failed to obtain the cumulative preferences", caught);
						if (caught instanceof AuthorizationException) {
							System.out.println("Please login to view cumulative preferences for " + project.getName());
						}
					}

					public void onSuccess(Object result) {
						values = (int[][]) result;

						String chartData = "";

						for(int j = 0; j < users.length; j++) {
							if(j == (users.length-1)) {
								for(int k = 0; k < goal_length; k++) {
									if(k == (goal_length-1)) {
										chartData = chartData + values[j][k+goal_index];
									} else
										chartData = chartData + values[j][k+goal_index] + ",";
								}
							} else {
								for(int k = 0; k < goal_length; k++) {
									if(k == (goal_length-1)) {
										chartData = chartData + values[j][k+goal_index];
									} else
										chartData = chartData + values[j][k+goal_index] + ",";
								}
								chartData = chartData + "|";
							}
						}
						String URL = "<img src = " + "'" + PREFIX_URL + "chxt=y,x&chs=900x325&cht=bhs&chd=t:" + chartData + "&chco=4d89f9,48e766,f468ca,c6d9fd,e74863,5cb3ff,e4ec57,f52887,57ecbf&chdlp=r&chds=0,250&chxl=0:|" + value_names + "|1:|0|75|150'" + " />";
						HTML html = new HTML(URL);
						panels[index].add(html);
						usedPanels[index] = true;				
					}
				});
			}
		});
	}

	private void setUpPanels(int numSecondOrder) {
		portal = new Portal();
		portal.setWidth(960);
		PortalColumn firstCol = new PortalColumn();
		p = new Portlet[numSecondOrder];
		panels = new VerticalPanel[numSecondOrder];
		for(int i = 0; i < (int)numSecondOrder; i++) {
			panels[i] = new VerticalPanel();
			p[i] = new Portlet();
			p[i].setTitle(secGoals[i].getName());
			p[i].setSize(380, 365);
			p[i].add(panels[i]);
			firstCol.add(p[i]);
		}
		portal.add(firstCol, new ColumnLayoutData(0.98));
		eastPanel.add(portal);
	}

	private void setUpReset(int numSecondOrder) {
		reset = new Item[numSecondOrder];
		Menu resetMenu = new Menu();
		for(int i = 0; i < numSecondOrder; i++) {
			final int index = i;
			reset[i] = new Item("Chart " + (i+1));
			reset[i].addListener(new BaseItemListenerAdapter() {
				public void onClick(BaseItem item, EventObject e) {
					panels[index].clear();
					p[index].setTitle("Chart " + (index+1));
					usedPanels[index] = false;
				}
			});
			resetMenu.addItem(reset[i]);
		}
		resetButton.setMenu(resetMenu);
		eastPanel.add(resetButton);
		horizontalPanel.add(eastPanel);
	}

	protected void addChildGoal(TreeNode parent, Goal goal, boolean recurse, int leaf, boolean firstLevel) {
		TreeNode node = new TreeNode(goal.getName());
		node.setUserObject(goal);
		if(leaf == 0) node.setChecked(true);
		parent.appendChild(node);

		if (recurse) {
			for (int i = 0; i < goal.getNumChildren(); i++) {
				addChildGoal(node, goal.getChild(i), true, goal.getChild(i).getNumChildren(),false);
			}
		}

		/* If this node, as identified by its option's id, was "expanded" in a
		 * previous incarnation of this tree, then expand it here again.
		 */
		if (expandedItems.contains(Integer.toString(goal.getId())))
			node.setExpanded(true);

		if(goal.getNumChildren() == 0 && !firstLevel) {
			for(int i = 0; i < 100; i++) {
				if(secGoals[i] == null) {
					secGoals[i] = goal.getParent();
				}
				if(secGoals[i].getId() == goal.getParent().getId()) break;
			}
		}
	}

	/**
	 * A command that refreshes the view (tree and chart).
	 */
	protected Command updateView = new Command() {
		public void execute() {
			tree.stopEditing();
			tree.clear();
			populateTree(project);
		}
	};

	public void onTreeItemSelected(TreeItem item) {
		//Do Nothing
	}

	/**
	 * Remember the expansion state of this item, so that we can later
	 * return the tree to the same state when it is refreshed.
	 *
	 * @param item the item whose expanded/folded state changed
	 */
	public void onTreeItemStateChanged(TreeItem item) {
		/* Remember the expansion state for later tree refresh, based on goal ID. */
		Goal g;
		if (item.getUserObject() instanceof Goal)
			g = (Goal) item.getUserObject();
		else
			return;

		if (item.getState())
			expandedItems.add(Integer.toString(g.getId()));
		else
			expandedItems.remove(Integer.toString(g.getId()));			
	}

	protected void populateTree(final ProjectInfo project) {

		final TreeItem rootItem = new TreeItem();
		rootItem.setText("Loading goals with preferences for " + currentUser.getName());
		tree.addItem(rootItem);

		DataService.App.getInstance().getPreferencesForUser(project.getId(), currentUser.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
			public void onFailure(Throwable caught)
			{
				GWT.log("Failed to populate the preferences table", caught);
				if (caught instanceof AuthorizationException) {
					rootItem.setText("Please login to view preferences for " + project.getName());
				}
			}

			public void onSuccess(Object result)
			{
				Preference[] prefs = (Preference[]) result;

				remainingBudget = maxBudget;
				prefsByGoal.clear();

				Preference pref;
				for (int i = 0; i < prefs.length; i++) {
					pref = prefs[i];
					prefsByGoal.put(Integer.toString(pref.getGoal().getId()), pref);
					remainingBudget -= pref.getValue();
				}

				loadGoalsWithPreferences(rootItem);
			}
		});
	}

	protected void loadGoalsWithPreferences(final TreeItem rootItem) {
		DataService.App.getInstance().getGoals(project.getId(), container.getCurrentAuthToken(), new AsyncCallback() {
			public void onFailure(Throwable caught) {
				GWT.log("Failed to populate the goals/preferences tree", caught);
				if (caught instanceof AuthorizationException) {
					rootItem.setText("Please login to view preferences for " + project.getName());
				}
			}

			public void onSuccess(Object result) {
				rootItem.setText("Preferences for " + currentUser.getName() + " - Remaining Budget: " + remainingBudget);

				Goal[] roots = (Goal[]) result;
				for (int i = 0; i < roots.length; i++) {
					addChild(rootItem, roots[i], true);
				}

				/* Expand the first and second levels */
				rootItem.setState(true, true);
				for (int i = 0; i < rootItem.getChildCount(); i++ ) {
					rootItem.getChild(i).setState(true, true);
				}
			}
		});
	}

	/**
	 * Adds the given Goal as a child TreeItem to the parent TreeItem.
	 * Optionally steps into the given Goal's children and does the same there,
	 * etc. recursively.
	 *
	 * @param parent  the tree node to act as parent for the newly added goal
	 * @param child   the goal to be added as a child node to the tree
	 * @param recurse if true, will call this method for the child and each of its children, etc.
	 *
	 * @return the aggregated preference value of this node
	 */
	protected int addChild(TreeItem parent, Goal child, boolean recurse) {
		TreeItem node = new TreeItem(child.getName());
		node.setUserObject(child);
		parent.addItem(node);

		int aggregateValue = 0;
		if (recurse) {
			for (int i = 0; i < child.getNumChildren(); i++)
				aggregateValue += addChild(node, child.getChild(i), true);
		}

		Preference pref;
		pref = (Preference) prefsByGoal.get(Integer.toString(child.getId()));
		if ( pref != null) {
			/* If this goal has a specific preference assigned, its value is taken
			 * as the associated preference. Otherwise its value is taken as the
			 * aggregated values of its sub-goals.
			 */
			aggregateValue = pref.getValue();
		}

		String label;
		label = child.getName();
		label += ": " + aggregateValue;
		node.setText(label);

		/* If this node, as identified by its goal's id, was "expanded" in a
		 * previous incarnation of this tree, then expand it here again.
		 */
		if (expandedItems.contains(Integer.toString(child.getId())))
			node.setState(true, false);

		return aggregateValue;
	}


	/**
	 * Display aggregate preference values, as a pie chart. All leaf
	 * nodes are aggregated and each of their parents is displayed as
	 * a piece of the pie.
	 */
	protected void populateChart()
	{
		//TODO: HOW TO GET getAggregatePreferencesForUser TO WORK? RIGHT NOW IT DOESN'T PULL THINGS CORRECTLY FROM DB.

		DataService.App.getInstance().getPreferencesForUser(project.getId(), currentUser.getId(), container.getCurrentAuthToken(), new AsyncCallback()
		{
			public void onFailure(Throwable caught)
			{
				GWT.log("Failed to populate the preferences chart", caught);
				if (caught instanceof AuthorizationException) {
					// TODO: do not use Window.alert for error feedback
					System.out.println("Please login to view preferences for " + project.getName());
				}
			}

			public void onSuccess(Object result)
			{
				Preference[] prefs = (Preference[]) result;
				drawChart(prefs);
			}
		});

	}

	//For the self tree
	protected void drawChart(Preference[] prefs)
	{
		int numPrefs = prefs.length;

		String[] xLabels = new String[numPrefs];
		for (int i = 0; i < numPrefs; i++) {
			xLabels[numPrefs-1-i] = prefs[i].getGoal().getName();
		}

		String value_names = "";

		for(int i=0; i < numPrefs; i++) {
			if(i == (numPrefs-1)) {
				value_names = value_names + xLabels[i];
			} else
				value_names = value_names + xLabels[i] + "|";
		}

		String chartData = "";

		for(int i = 0; i < numPrefs; i++) {
			if(i == (numPrefs-1)) {
				chartData = chartData + prefs[i].getValue();
			} else {
				chartData = chartData + prefs[i].getValue() + ",";
			}
		}

		String URL = "<img src = " + "'" + PREFIX_URL + "chxt=y,x&chs=900x325&cht=bhs&chd=t:" + chartData + "&chco=4d89f9,48e766,f468ca,c6d9fd,e74863,5cb3ff,e4ec57,f52887,57ecbf&chdlp=r&chds=0," + 100 + "&chxl=0:|" + value_names + "|1:|0|" + 50 + "|" + 100 + "'" + " />";
		HTML html = new HTML(URL);			

		int index = returnAvailablePanel();
		if(index == -1) {
			Window alert = new Window();
			alert.setWidth(300);
			alert.setHeight(300);
			alert.setPlain(false);
			alert.setHtml("<p><b><font size = 2> Please clear one or more of your graphs. </b></font></p>" +
			"<p><img src = 'http://img.quamut.com/chart/771/14washing_windows.jpg' />");
			alert.setClosable(true);
			alert.setDraggable(false);
			alert.setResizable(false);
			alert.setCloseAction(Window.HIDE);
			alert.show();
		} else {
			panels[index].add(html);
		}
	}


	/**
	 * Lets you edit a user preference value for a goal.
	 * Only matches to leaf goals.
	 */
	public class PreferenceEdit extends DefaultItemEditor {
		protected ProjectInfo project;

		public PreferenceEdit(Command onSuccess, ProjectInfo p) {
			super(DefaultItemEditor.iconPathEdit, "Set Preference", onSuccess);
			this.project = p;
		}

		protected Widget getInputWidget(Object userObject) {
			if (userObject == null || !(userObject instanceof Goal))
				throw new IllegalArgumentException("userObject must be a valid Goal");

			return configureWidget((Goal) userObject);
		}
		public boolean matchesItem(TreeItem item) {
			/* Only display this for leaf items */
			return item.getChildCount() == 0;
		}

		protected Widget configureWidget(final Goal goal) {
			/* get preference for this goal */

			final Preference pref;
			pref = (Preference) prefsByGoal.get(Integer.toString(goal.getId()));

			VerticalPanel vp = new VerticalPanel();

			final ListBox lb = new ListBox();
			fillAvailablePreferenceValues(lb, pref);   /* Also sets the selected index */
			vp.add(lb);

			Button submitValue = new Button("Submit", new ButtonListenerAdapter() {
				public void onClick(Button button, EventObject e) {
					int newValue = -1;
					newValue = Integer.parseInt(lb.getValue(lb.getSelectedIndex()));
					updatePreference(pref, newValue);
					edit_window.setVisible(true);
				}
			});
			vp.add(submitValue);

			edit_window.setVisible(false);

			return vp;
		}

		protected void updatePreference(final Preference pref, int newValue) {
			if ( newValue == pref.getValue() ) {
				return;
			}

			DataWriteService.App.getInstance().updatePreference(project.getId(), pref.getGoal().getId(), pref.getUser().getId(), newValue, container.getCurrentAuthToken(), this);

		}

		protected void fillAvailablePreferenceValues(final ListBox choices, Preference pref) {
			choices.addItem("0");
			choices.setSelectedIndex(0);

			int currentVal = pref.getValue();
			int maxVal = currentVal + remainingBudget;
			for (int i = 1; i <= maxVal; i++ ) {
				choices.addItem(Integer.toString(i));
				if ( i == currentVal )
					choices.setSelectedIndex(i);
			}
		}
	}
}

/* Creates pie chart.
int numPrefs = prefs.length;
String[] goalNames = new String[numPrefs];
double[] goalPrefs = new double[numPrefs];

Preference p;
for (int i = 0; i < numPrefs; i++ ) {
	p = prefs[i];
	goalNames[i] = p.getGoal().getName();
	goalPrefs[i] = p.getValue();
}

String preference_values = "";
for(int i=0; i < numPrefs; i++ ) {
	if(i == (numPrefs-1)) {
		preference_values = preference_values + goalPrefs[i];
	} else
		preference_values = preference_values + goalPrefs[i] + ",";
}

String preference_names = "";
for(int i=0; i < numPrefs; i++) {
	if(i == (numPrefs-1)) {
		preference_names = preference_names + goalNames[i];
	} else
		preference_names = preference_names + goalNames[i] + "|";

}

String URL = "<img src = " + "'" + PREFIX_URL + "chs=600x300&chd=t:" + preference_values + "&cht=p&chco=2fba10&chl=" + preference_names + "'" + " />";
HTML html = new HTML(URL); */


/* CODE FOR TEXTPANEL INSTEAD OF BOX LIST */

/*final FormPanel vp = new FormPanel();
vp.setTitle("Edit Goal: " + goal.getName());
vp.setFrame(true);
vp.setWidth(330);
vp.setHeight(120);
vp.setPaddings(20);
vp.setLabelWidth(120);
final TextField tf = new TextField("Integer (0-100) ", "preference_value", 150);
tf.setSelectOnFocus(true);
tf.setValue(String.valueOf(pref.getValue()));
tf.setMaxLength(3);
tf.setMaxLengthText("Please enter a sensible integer.");
vp.add(tf);*/
/*Button submitValue = new Button("Submit", new ButtonListenerAdapter() {
public void onClick(Button button, EventObject e) {
	int newValue = -1;
    newValue = Integer.parseInt(tf.getText());
    updatePreference(pref, newValue);
    edit_window.setVisible(true);
}
});
vp.addButton(submitValue);*/